/****************************************************************************
 * Elisa 1.0                                                                *
 *--------------------------------------------------------------------------*
 * Author: Laurent Granvilliers, Vincent Sorin                              *
 * Copyright (c) 2004 Laboratoire d'Informatique de Nantes Atlantique       *
 *--------------------------------------------------------------------------*
 * Elisa is distributed WITHOUT ANY WARRANTY. Read the associated           *
 * COPYRIGHT file for more details.                                         *
 *--------------------------------------------------------------------------*
 * elisa_real_expr.cpp                                                      *
 ****************************************************************************/

#include "elisa_real_expr.h"


USING_ELISA


//--------------------------------------------------------------------------
// class RealExprRep
//--------------------------------------------------------------------------

RealExprRep::RealExprRep()
	: SymbolicExpressionRep()
{
	_nref = 0;
}

RealExprRep::RealExprRep(const RealExprRep & e)
	: SymbolicExpressionRep()
{
	_nref = 0;
}

RealExprRep::~RealExprRep()
{
}

int RealExprRep::priority() const
{
	return 0;
}

void RealExprRep::toVariables(VarImplSet & s)
{
	ToVariableVisitor v(&s);
	this->visit(v);
}

void RealExprRep::initVariableImplementation(SearchTree & searchTree)
{
	initVariableImplementationVisitor v(&searchTree);
	this->visit(v);
}

int RealExprRep::multiplicity(VariableImpl & v)
{
	RealVarImpl *rv = dynamic_cast<RealVarImpl *>(&v);
	if (rv != 0) {
		MultiplicityVisitor mv(*rv);
		this->visit(mv);
		return mv.getMultiplicity();
	}
	// ishii
	return 0;
}

//--------------------------------------------------------------------------
// class RealExprVisitor
//--------------------------------------------------------------------------

void RealExprVisitor::visit(RealExprRep & e)
{
	e.visit(*this);
}


/// Call of the visitor from a constant expression
void RealExprVisitor::visited(RealExprRepConst & e)
{
}


/// Call of the visitor from an interval representing a constant
void RealExprVisitor::visited(RealExprRepIntervalConst & e)
{
}


/// Call of the visitor from an expression representing a variable
void RealExprVisitor::visited(RealExprRepVar & e)
{
}


/// Call of the visitor from an expression +(f,g)
void RealExprVisitor::visited(RealExprRepAdd & e)
{
	visit(*e.right());
	visit(*e.left());
}

/// Call of the visitor from an expression min(f,g)
void RealExprVisitor::visited(RealExprRepMin & e)
{
	visit(*e.right());
	visit(*e.left());
}

/// Call of the visitor from an expression max(f,g)
void RealExprVisitor::visited(RealExprRepMax & e)
{
	visit(*e.right());
	visit(*e.left());
}

/// Call of the visitor from an expression +(f1,f2,...,fN)
void RealExprVisitor::visited(RealExprRepSum & e)
{
	for (int i = 1; i <= e.arity(); ++i) {
		visit(*(e.child(i)));
	}
}


/// Call of the visitor from an expression *(f,g)
void RealExprVisitor::visited(RealExprRepMul & e)
{
	visit(*e.right());
	visit(*e.left());
}


/// Call of the visitor from an expression *(f1,f2,...,fN)
void RealExprVisitor::visited(RealExprRepProd & e)
{
	for (int i = 1; i <= e.arity(); ++i) {
		visit(*(e.child(i)));
	}
}


/// Call of the visitor from an expression -(f,g)
void RealExprVisitor::visited(RealExprRepSub & e)
{
	visit(*e.right());
	visit(*e.left());
}


/// Call of the visitor from an expression -f
void RealExprVisitor::visited(RealExprRepOpp & e)
{
	visit(*e.child());
}


/// Call of the visitor from an expression /(f,g)
void RealExprVisitor::visited(RealExprRepDiv & e)
{
	visit(*e.right());
	visit(*e.left());
}


/// Call of the visitor from an expression sqr(f)
void RealExprVisitor::visited(RealExprRepSqr & e)
{
	visit(*e.child());
}


/// Call of the visitor from an expression sqrt(f)
void RealExprVisitor::visited(RealExprRepSqrt & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression abs(f)
void RealExprVisitor::visited(RealExprRepAbs & e)
{
	visit(*e.child());
}


/// Call of the visitor from an expression pow(f,n)
void RealExprVisitor::visited(RealExprRepPow & e)
{
	visit(*e.child());
}


/// Call of the visitor from an expression log(f)
void RealExprVisitor::visited(RealExprRepLog & e)
{
	visit(*e.child());
}


/// Call of the visitor from an expression exp(f)
void RealExprVisitor::visited(RealExprRepExp & e)
{
	visit(*e.child());
}


/// Call of the visitor from an expression cos(f)
void RealExprVisitor::visited(RealExprRepCos & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression cosh(f)
void RealExprVisitor::visited(RealExprRepCosh & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression cos(f)
void RealExprVisitor::visited(RealExprRepACos & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression cosh(f)
void RealExprVisitor::visited(RealExprRepACosh & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression sin(f)
void RealExprVisitor::visited(RealExprRepSin & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression sinh(f)
void RealExprVisitor::visited(RealExprRepSinh & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression asin(f)
void RealExprVisitor::visited(RealExprRepASin & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression asinh(f)
void RealExprVisitor::visited(RealExprRepASinh & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression tan(f)
void RealExprVisitor::visited(RealExprRepTan & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression tanh(f)
void RealExprVisitor::visited(RealExprRepTanh & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression atan(f)
void RealExprVisitor::visited(RealExprRepATan & e)
{
	visit(*e.child());
}

/// Call of the visitor from an expression atanh(f)
void RealExprVisitor::visited(RealExprRepATanh & e)
{
	visit(*e.child());
}

//--------------------------------------------------------------------------
// class RealExprVisitor
//--------------------------------------------------------------------------
RealExprVisitor::RealExprVisitor()
{
}

RealExprVisitor::~RealExprVisitor()
{
}


//--------------------------------------------------------------------------
// class ToVariableVisitor
//--------------------------------------------------------------------------

/// Call of the visitor from an expression representing a variable
void ToVariableVisitor::visited(RealExprRepVar & e)
{
	_s->insert(e.variable().getImplementation());
}


//--------------------------------------------------------------------------
// class initVariableImplementationVisitor
//--------------------------------------------------------------------------

/// Call of the visitor from an expression representing a variable
void initVariableImplementationVisitor::visited(RealExprRepVar & e)
{
	if (e.variable().getImplementation() == NULL) {
		e.variable().buildImplementation(_st);
	}
}

//--------------------------------------------------------------------------
// class MultiplicityVisitor
//--------------------------------------------------------------------------

/// Call of the visitor from an expression representing a variable
void MultiplicityVisitor::visited(RealExprRepVar & e)
{
	if (*(e.variable().getImplementation()) == *_v) {
		_counter++;
	}
}


//--------------------------------------------------------------------------
// class RealExprRepVar
//--------------------------------------------------------------------------

RealExprRepVar::RealExprRepVar(Variable & v)
	: _v(&v)
{
}

RealExprRepVar::RealExprRepVar (Variable *v)
	: _v(v)
{
}

RealExprRepVar::RealExprRepVar (const RealExprRepVar & e)
	: RealExprRep(e), _v(e._v)
{
}

RealExprRepVar::~RealExprRepVar ()
{
}

RealExprRepVar &
RealExprRepVar::operator=(const RealExprRepVar & e)
{
	_v = e._v;
	return(*this);
}

Variable & RealExprRepVar::variable()
{
	return(*_v);
}

const Variable & RealExprRepVar::variable() const
{
	return(*_v);
}

int RealExprRepVar::priority() const
{
	return 4;
}

RealExprRep *RealExprRepVar::copy() const
{
	return new RealExprRepVar(_v);
}

void RealExprRepVar::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

void RealExprRepVar::print(std::ostream & os) const
{
	_v->print(os);
}


//--------------------------------------------------------------------------
// class RealExprRepConst
//--------------------------------------------------------------------------

RealExprRepConst::RealExprRepConst (double i)
	: _value(RealConstant(i))
{
}

RealExprRepConst::RealExprRepConst (const RealConstant & c)
	: _value(c)
{
}

RealExprRepConst::RealExprRepConst (const RealExprRepConst & e)
	: RealExprRep(e), _value(e._value)
{
}

RealExprRepConst::~RealExprRepConst ()
{
}

RealExprRepConst &
RealExprRepConst::operator=(const RealExprRepConst & e)
{
	_value = e._value;
	return(*this);
}

double RealExprRepConst::value() const
{
	return _value.getValue();
}

Interval RealExprRepConst::getInterval() const
{
	return _value.getInterval();
}

int RealExprRepConst::priority() const
{
	return 4;
}

RealExprRep *RealExprRepConst::copy() const
{
	return new RealExprRepConst(_value);
}

void RealExprRepConst::visit(RealExprVisitor & v)
{
	v.visited(*this);
}


void RealExprRepConst::print(std::ostream & os) const
{
	_value.print(os);
}

//--------------------------------------------------------------------------
// class RealExprRepIntervalConst
//--------------------------------------------------------------------------

RealExprRepIntervalConst::RealExprRepIntervalConst (double i, double s)
{
	_value = IntervalConstant(i, s);
}

RealExprRepIntervalConst::RealExprRepIntervalConst (const IntervalConstant & c)
{
	_value = c;
}

RealExprRepIntervalConst::RealExprRepIntervalConst (const RealExprRepIntervalConst & e) : RealExprRep(e)
{
	_value = e._value;
}

RealExprRepIntervalConst::~RealExprRepIntervalConst ()
{
}

RealExprRepIntervalConst &
RealExprRepIntervalConst::operator=(const RealExprRepIntervalConst & e)
{
	_value = e._value;
	return(*this);
}

double RealExprRepIntervalConst::inf() const
{
	return _value.getInf();
}

Interval RealExprRepIntervalConst::getInterval() const
{
	return _value.getInterval();
}


double RealExprRepIntervalConst::sup() const
{
	return _value.getSup();
}

int RealExprRepIntervalConst::priority() const
{
	return 4;
}

RealExprRep *RealExprRepIntervalConst::copy() const
{
	return new RealExprRepIntervalConst(_value);
}

void RealExprRepIntervalConst::visit(RealExprVisitor & v)
{
	v.visited(*this);
}


void RealExprRepIntervalConst::print(std::ostream & os) const
{
	_value.print(os);
}


//--------------------------------------------------------------------------
// class RealExprRepOp
//--------------------------------------------------------------------------
RealExprRepOp::RealExprRepOp(const std::string & op,
                             int priority,
                             bool prefix)
	: _op(op), _prefix(prefix), _priority(priority)
{
}

RealExprRepOp::~RealExprRepOp()
{
	for (unsigned int i = 0; i < _exprs.size(); ++i)
		_exprs[i]->delRef();
}

void RealExprRepOp::addChild(RealExprRep *e)
{
	e->addRef();
	_exprs.push_back(e);
}

int RealExprRepOp::arity() const
{
	return _exprs.size();
}

RealExprRep *RealExprRepOp::child(int i)
{
	return _exprs[i - 1];
}

RealExprRep *RealExprRepOp::const_child(int i) const
{
	return _exprs[i - 1];
}

void RealExprRepOp::print(std::ostream & os) const
{
	if (_prefix) {
		os << _op << "(";

		for (unsigned int i = 0; i < _exprs.size(); ++i) {
			_exprs[i]->print(os);
			if (i < (_exprs.size() - 1)) {
				os << ',';
			}
		}
		os << ")";
	} else {
		unsigned int i = 0;
		do {
			if (_exprs[i]->priority() <= _priority) {
				os << "(";
				_exprs[i]->print(os);
				os << ")";
			} else _exprs[i]->print(os);

			if (i < (_exprs.size() - 1)) {
				os << _op;
			}

			++i;
		} while (i < _exprs.size());
	}
}


int RealExprRepOp::priority() const
{
	return _priority;
}


//--------------------------------------------------------------------------
// class RealExprRepBinaryOp
//--------------------------------------------------------------------------
RealExprRepBinaryOp::RealExprRepBinaryOp (RealExprRep *l, RealExprRep *r,
                                          const std::string & op,
                                          int priority)
	: RealExprRepOp(op, priority, false)
{
	addChild(l);
	addChild(r);
}

RealExprRepBinaryOp::~RealExprRepBinaryOp ()
{
}

RealExprRep *RealExprRepBinaryOp::left()
{
	return _exprs[0];
}

RealExprRep *RealExprRepBinaryOp::const_left() const
{
	return _exprs[0];
}

RealExprRep *RealExprRepBinaryOp::right()
{
	return _exprs[1];
}

RealExprRep *RealExprRepBinaryOp::const_right() const
{
	return _exprs[1];
}


//--------------------------------------------------------------------------
// class RealExprRepUnaryOp
//--------------------------------------------------------------------------
RealExprRepUnaryOp::RealExprRepUnaryOp (RealExprRep *c,
                                        const std::string & op,
                                        int priority) :
	RealExprRepOp(op, priority, true)
{
	addChild(c);
}

RealExprRepUnaryOp::~RealExprRepUnaryOp ()
{
}

RealExprRep *RealExprRepUnaryOp::child()
{
	return _exprs[0];
}

RealExprRep *RealExprRepUnaryOp::const_child() const
{
	return _exprs[0];
}


//--------------------------------------------------------------------------
// class RealExprRepMin
//--------------------------------------------------------------------------
RealExprRepMin::RealExprRepMin (RealExprRep & l,   // left sub-term
								RealExprRep & r)   // right sub-term
								: RealExprRepBinaryOp(&l, &r, "min", 3)
{
}


RealExprRepMin::RealExprRepMin (RealExprRep *l,   // left sub-term
								RealExprRep *r)   // right sub-term
								: RealExprRepBinaryOp(l, r, "min", 3)
{
}


RealExprRepMin::RealExprRepMin (const RealExprRepMin & e)
: RealExprRepBinaryOp(e.const_left(), e.const_right(), "min", 3)
{
}

RealExprRepMin::~RealExprRepMin()
{
}


void RealExprRepMin::visit(RealExprVisitor & v)
{
	v.visited(*this);
}


RealExprRep *RealExprRepMin::copy() const
{
	return new RealExprRepMin(const_left()->copy(), const_right()->copy());
}


//--------------------------------------------------------------------------
// class RealExprRepMax
//--------------------------------------------------------------------------
RealExprRepMax::RealExprRepMax (RealExprRep & l,   // left sub-term
								RealExprRep & r)   // right sub-term
								: RealExprRepBinaryOp(&l, &r, "max", 3)
{
}


RealExprRepMax::RealExprRepMax (RealExprRep *l,   // left sub-term
								RealExprRep *r)   // right sub-term
								: RealExprRepBinaryOp(l, r, "max", 3)
{
}


RealExprRepMax::RealExprRepMax (const RealExprRepMax & e)
: RealExprRepBinaryOp(e.const_left(), e.const_right(), "max", 3)
{
}

RealExprRepMax::~RealExprRepMax()
{
}


void RealExprRepMax::visit(RealExprVisitor & v)
{
	v.visited(*this);
}


RealExprRep *RealExprRepMax::copy() const
{
	return new RealExprRepMax(const_left()->copy(), const_right()->copy());
}


//--------------------------------------------------------------------------
// class RealExprRepAdd
//--------------------------------------------------------------------------
RealExprRepAdd::RealExprRepAdd (RealExprRep & l,   // left sub-term
								RealExprRep & r)   // right sub-term
								: RealExprRepBinaryOp(&l, &r, "+", 1)
{
}


RealExprRepAdd::RealExprRepAdd (RealExprRep *l,   // left sub-term
								RealExprRep *r)   // right sub-term
								: RealExprRepBinaryOp(l, r, "+", 1)
{
}


RealExprRepAdd::RealExprRepAdd (const RealExprRepAdd & e)
: RealExprRepBinaryOp(e.const_left(), e.const_right(), "+", 1)
{
}

RealExprRepAdd::~RealExprRepAdd()
{
}


void RealExprRepAdd::visit(RealExprVisitor & v)
{
	v.visited(*this);
}


RealExprRep *RealExprRepAdd::copy() const
{
	return new RealExprRepAdd(const_left()->copy(), const_right()->copy());
}

//RealExprRepAdd&
//RealExprRepAdd::operator= (const RealExprRepAdd& e) {}


//--------------------------------------------------------------------------
// class RealExprRepSum
//--------------------------------------------------------------------------
RealExprRepSum::RealExprRepSum () :
	RealExprRepOp("+", 1, false)
{
}

RealExprRepSum::~RealExprRepSum ()
{
}

void RealExprRepSum::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepSum::copy() const
{
	RealExprRepSum *rep = new RealExprRepSum;
	for (int i = 1; i <= arity(); ++i)
		rep->addChild(const_child(i)->copy());
	return rep;
}

//RealExprRepSum&
//RealExprRepSum::operator= (const RealExprRepSum& e) {}


//--------------------------------------------------------------------------
// class RealExprRepMul
//--------------------------------------------------------------------------
RealExprRepMul::RealExprRepMul (RealExprRep & l,   // left-sub-term
                                RealExprRep & r)   // right sub-term
	: RealExprRepBinaryOp(&l, &r, "*", 2)
{
}


RealExprRepMul::RealExprRepMul (RealExprRep *l,   // left-sub-term
                                RealExprRep *r)   // right sub-term
	: RealExprRepBinaryOp(l, r, "*", 2)
{
}


RealExprRepMul::RealExprRepMul (const RealExprRepMul & e) :
	RealExprRepBinaryOp(e.const_left(), e.const_right(), "*", 2)
{
}

RealExprRepMul::~RealExprRepMul ()
{
}

void RealExprRepMul::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepMul::copy() const
{
	return new RealExprRepMul(const_left()->copy(),
	                          const_right()->copy());
}

//RealExprRepMul&
//RealExprRepMul::operator= (const RealExprRepMul& e) {}


//--------------------------------------------------------------------------
// class RealExprRepProd
//--------------------------------------------------------------------------
RealExprRepProd::RealExprRepProd () :
	RealExprRepOp("*", 2, false)
{
}

RealExprRepProd::~RealExprRepProd ()
{
}

void RealExprRepProd::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepProd::copy() const
{
	RealExprRepProd *rep = new RealExprRepProd;
	for (int i = 1; i <= arity(); ++i)
		rep->addChild(const_child(i)->copy());
	return rep;
}

//RealExprRepProd&
//RealExprRepProd::operator= (const RealExprRepProd& e) {}


//--------------------------------------------------------------------------
// class RealExprRepSub
//--------------------------------------------------------------------------
RealExprRepSub::RealExprRepSub (RealExprRep & l,   // left-sub-term
                                RealExprRep & r) :  // right sub-term
	                                                RealExprRepBinaryOp(&l, &r, "-", 1)
{
}

RealExprRepSub::RealExprRepSub (RealExprRep *l,   // left-sub-term
                                RealExprRep *r) :  // right sub-term
	                                               RealExprRepBinaryOp(l, r, "-", 1)
{
}

RealExprRepSub::RealExprRepSub (const RealExprRepSub & e) :
	RealExprRepBinaryOp(e.const_left(), e.const_right(), "-", 1)
{
}

RealExprRepSub::~RealExprRepSub ()
{
}

void RealExprRepSub::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepSub::copy() const
{
	return new RealExprRepSub(const_left()->copy(),
	                          const_right()->copy());
}

//RealExprRepSub&
//RealExprRepSub::operator= (const RealExprRepSub& e) {}


//--------------------------------------------------------------------------
// class RealExprRepOpp
//--------------------------------------------------------------------------
RealExprRepOpp::RealExprRepOpp (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "-", 1)
{
}

RealExprRepOpp::RealExprRepOpp (RealExprRep *e) :
	RealExprRepUnaryOp(e, "-", 1)
{
}

RealExprRepOpp::RealExprRepOpp (const RealExprRepOpp & e) :
	RealExprRepUnaryOp(e.const_child(), "-", 1)
{
}

RealExprRepOpp::~RealExprRepOpp ()
{
}

void RealExprRepOpp::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepOpp::copy() const
{
	return new RealExprRepOpp(const_child()->copy());
}

//RealExprRepOpp&
//RealExprRepOpp::operator= (const RealExprRepOpp& e) {}


//--------------------------------------------------------------------------
// class RealExprRepDiv
//--------------------------------------------------------------------------
RealExprRepDiv::RealExprRepDiv (RealExprRep & l,   // left sub-term
                                RealExprRep & r) :  // right sub-term
	                                                RealExprRepBinaryOp(&l, &r, "/", 2)
{
}

RealExprRepDiv::RealExprRepDiv (RealExprRep *l,  // left sub-term
                                RealExprRep *r) : // right sub-term
	                                              RealExprRepBinaryOp(l, r, "/", 2)
{
}

RealExprRepDiv::RealExprRepDiv (const RealExprRepDiv & e) :
	RealExprRepBinaryOp(e.const_left(), e.const_right(), "/", 2)
{
}

RealExprRepDiv::~RealExprRepDiv ()
{
}

void RealExprRepDiv::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepDiv::copy() const
{
	return new RealExprRepDiv(const_left()->copy(), const_right()->copy());
}

//RealExprRepDiv&
//RealExprRepDiv::operator= (const RealExprRepDiv& e) {}


//--------------------------------------------------------------------------
// class RealExprRepSqr
//--------------------------------------------------------------------------
RealExprRepSqr::RealExprRepSqr (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "sqr", 3)
{
}

RealExprRepSqr::RealExprRepSqr (RealExprRep *e) :
	RealExprRepUnaryOp(e, "sqr", 3)
{
}

RealExprRepSqr::RealExprRepSqr (const RealExprRepSqr & e) :
	RealExprRepUnaryOp(e.const_child(), "sqr", 3)
{
}

RealExprRepSqr::~RealExprRepSqr ()
{
}

void RealExprRepSqr::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepSqr::copy() const
{
	return new RealExprRepSqr(const_child()->copy());
}

//RealExprRepSqr&
//RealExprRepSqr::operator= (const RealExprRepSqr& e) {}


//--------------------------------------------------------------------------
// class RealExprRepSqrt
//--------------------------------------------------------------------------
RealExprRepSqrt::RealExprRepSqrt (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "sqrt", 3)
{
}

RealExprRepSqrt::RealExprRepSqrt (RealExprRep *e) :
	RealExprRepUnaryOp(e, "sqrt", 3)
{
}

RealExprRepSqrt::RealExprRepSqrt (const RealExprRepSqrt & e) :
	RealExprRepUnaryOp(e.const_child(), "sqrt", 3)
{
}

RealExprRepSqrt::~RealExprRepSqrt ()
{
}

void RealExprRepSqrt::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepSqrt::copy() const
{
	return new RealExprRepSqrt(const_child()->copy());
}

//RealExprRepSqrt&
//RealExprRepSqrt::operator= (const RealExprRepSqrt& e) {}


//--------------------------------------------------------------------------
// class RealExprRepPow
//--------------------------------------------------------------------------
RealExprRepPow::RealExprRepPow (RealExprRep & e, int n) :
	RealExprRepUnaryOp(&e, "pow", 3)
{
	_n = n;
}

RealExprRepPow::RealExprRepPow (RealExprRep *e, int n) :
	RealExprRepUnaryOp(e, "pow", 3)
{
	_n = n;
}

RealExprRepPow::RealExprRepPow (const RealExprRepPow & e) :
	RealExprRepUnaryOp(e.const_child(), "pow", 3)
{
	_n = e._n;
}

RealExprRepPow::~RealExprRepPow ()
{
}

int RealExprRepPow::exponent() const
{
	return _n;
}

void RealExprRepPow::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

void RealExprRepPow::print(std::ostream & os) const
{
	os << "pow(";
	const_child()->print(os);
	os << "," << _n << ")";
}

RealExprRep *RealExprRepPow::copy() const
{
	return new RealExprRepPow(const_child()->copy(), _n);
}

//RealExprRepPow&
//RealExprRepPow::operator= (const RealExprRepPow& e) {}


//--------------------------------------------------------------------------
// class RealExprRepLog
//--------------------------------------------------------------------------
RealExprRepLog::RealExprRepLog (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "log", 3)
{
}

RealExprRepLog::RealExprRepLog (RealExprRep *e) :
	RealExprRepUnaryOp(e, "log", 3)
{
}

RealExprRepLog::RealExprRepLog (const RealExprRepLog & e) :
	RealExprRepUnaryOp(e.const_child(), "log", 3)
{
}

RealExprRepLog::~RealExprRepLog ()
{
}

void RealExprRepLog::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepLog::copy() const
{
	return new RealExprRepLog(const_child()->copy());
}

//RealExprRepLog&
//RealExprRepLog::operator= (const RealExprRepLog& e) {}


//--------------------------------------------------------------------------
// class RealExprRepExp
//--------------------------------------------------------------------------
RealExprRepExp::RealExprRepExp (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "exp", 3)
{
}


RealExprRepExp::RealExprRepExp (RealExprRep *e) :
	RealExprRepUnaryOp(e, "exp", 3)
{
}


RealExprRepExp::RealExprRepExp (const RealExprRepExp & e) :
	RealExprRepUnaryOp(e.const_child(), "exp", 3)
{
}

RealExprRepExp::~RealExprRepExp ()
{
}

void RealExprRepExp::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepExp::copy() const
{
	return new RealExprRepExp(const_child()->copy());
}

//RealExprRepExp&
//RealExprRepExp::operator= (const RealExprRepExp& e) {}


//--------------------------------------------------------------------------
// class RealExprRepCos
//--------------------------------------------------------------------------
RealExprRepCos::RealExprRepCos (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "cos", 3)
{
}

RealExprRepCos::RealExprRepCos (RealExprRep *e) :
	RealExprRepUnaryOp(e, "cos", 3)
{
}

RealExprRepCos::RealExprRepCos (const RealExprRepCos & e) :
	RealExprRepUnaryOp(e.const_child(), "cos", 3)
{
}

RealExprRepCos::~RealExprRepCos ()
{
}

void RealExprRepCos::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepCos::copy() const
{
	return new RealExprRepCos(const_child()->copy());
}

//RealExprRepCos&
//RealExprRepCos::operator= (const RealExprRepCos& e) {}

//--------------------------------------------------------------------------
// class RealExprRepCosh
//--------------------------------------------------------------------------
RealExprRepCosh::RealExprRepCosh (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "cosh", 3)
{
}

RealExprRepCosh::RealExprRepCosh (RealExprRep *e) :
	RealExprRepUnaryOp(e, "cosh", 3)
{
}

RealExprRepCosh::RealExprRepCosh (const RealExprRepCosh & e) :
	RealExprRepUnaryOp(e.const_child(), "cosh", 3)
{
}

RealExprRepCosh::~RealExprRepCosh ()
{
}

void RealExprRepCosh::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepCosh::copy() const
{
	return new RealExprRepCosh(const_child()->copy());
}

//RealExprRepCosh&
//RealExprRepCosh::operator= (const RealExprRepCosh& e) {}

//--------------------------------------------------------------------------
// class RealExprRepACos
//--------------------------------------------------------------------------
RealExprRepACos::RealExprRepACos (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "acos", 3)
{
}

RealExprRepACos::RealExprRepACos (RealExprRep *e) :
	RealExprRepUnaryOp(e, "acos", 3)
{
}

RealExprRepACos::RealExprRepACos (const RealExprRepACos & e) :
	RealExprRepUnaryOp(e.const_child(), "acos", 3)
{
}

RealExprRepACos::~RealExprRepACos ()
{
}

void RealExprRepACos::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepACos::copy() const
{
	return new RealExprRepACos(const_child()->copy());
}

//RealExprRepACos&
//RealExprRepACos::operator= (const RealExprRepACos& e) {}

//--------------------------------------------------------------------------
// class RealExprRepACosh
//--------------------------------------------------------------------------
RealExprRepACosh::RealExprRepACosh (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "acosh", 3)
{
}

RealExprRepACosh::RealExprRepACosh (RealExprRep *e) :
	RealExprRepUnaryOp(e, "acosh", 3)
{
}

RealExprRepACosh::RealExprRepACosh (const RealExprRepACosh & e) :
	RealExprRepUnaryOp(e.const_child(), "acosh", 3)
{
}

RealExprRepACosh::~RealExprRepACosh ()
{
}

void RealExprRepACosh::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepACosh::copy() const
{
	return new RealExprRepACosh(const_child()->copy());
}

//RealExprRepACosh&
//RealExprRepACosh::operator= (const RealExprRepACosh& e) {}

//--------------------------------------------------------------------------
// class RealExprRepSin
//--------------------------------------------------------------------------
RealExprRepSin::RealExprRepSin (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "sin", 3)
{
}

RealExprRepSin::RealExprRepSin (RealExprRep *e) :
	RealExprRepUnaryOp(e, "sin", 3)
{
}

RealExprRepSin::RealExprRepSin (const RealExprRepSin & e) :
	RealExprRepUnaryOp(e.const_child(), "sin", 3)
{
}

RealExprRepSin::~RealExprRepSin ()
{
}

void RealExprRepSin::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepSin::copy() const
{
	return new RealExprRepSin(const_child()->copy());
}

//RealExprRepSin&
//RealExprRepSin::operator= (const RealExprRepSin& e) {}


//--------------------------------------------------------------------------
// class RealExprRepSinh
//--------------------------------------------------------------------------
RealExprRepSinh::RealExprRepSinh (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "sinh", 3)
{
}

RealExprRepSinh::RealExprRepSinh (RealExprRep *e) :
	RealExprRepUnaryOp(e, "sinh", 3)
{
}

RealExprRepSinh::RealExprRepSinh (const RealExprRepSinh & e) :
	RealExprRepUnaryOp(e.const_child(), "sinh", 3)
{
}

RealExprRepSinh::~RealExprRepSinh ()
{
}

void RealExprRepSinh::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepSinh::copy() const
{
	return new RealExprRepSinh(const_child()->copy());
}

//RealExprRepSinh&
//RealExprRepSinh::operator= (const RealExprRepSinh& e) {}


//--------------------------------------------------------------------------
// class RealExprRepASin
//--------------------------------------------------------------------------
RealExprRepASin::RealExprRepASin (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "asin", 3)
{
}

RealExprRepASin::RealExprRepASin (RealExprRep *e) :
	RealExprRepUnaryOp(e, "asin", 3)
{
}

RealExprRepASin::RealExprRepASin (const RealExprRepASin & e) :
	RealExprRepUnaryOp(e.const_child(), "asin", 3)
{
}

RealExprRepASin::~RealExprRepASin ()
{
}

void RealExprRepASin::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepASin::copy() const
{
	return new RealExprRepASin(const_child()->copy());
}

//RealExprRepASin&
//RealExprRepASin::operator= (const RealExprRepASin& e) {}


//--------------------------------------------------------------------------
// class RealExprRepASinh
//--------------------------------------------------------------------------
RealExprRepASinh::RealExprRepASinh (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "asinh", 3)
{
}

RealExprRepASinh::RealExprRepASinh (RealExprRep *e) :
	RealExprRepUnaryOp(e, "asinh", 3)
{
}

RealExprRepASinh::RealExprRepASinh (const RealExprRepASinh & e) :
	RealExprRepUnaryOp(e.const_child(), "asinh", 3)
{
}

RealExprRepASinh::~RealExprRepASinh ()
{
}

void RealExprRepASinh::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepASinh::copy() const
{
	return new RealExprRepASinh(const_child()->copy());
}

//RealExprRepASinh&
//RealExprRepASinh::operator= (const RealExprRepASinh& e) {}


//--------------------------------------------------------------------------
// class RealExprRepTan
//--------------------------------------------------------------------------
RealExprRepTan::RealExprRepTan (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "tan", 3)
{
}

RealExprRepTan::RealExprRepTan (RealExprRep *e) :
	RealExprRepUnaryOp(e, "tan", 3)
{
}

RealExprRepTan::RealExprRepTan (const RealExprRepTan & e) :
	RealExprRepUnaryOp(e.const_child(), "tan", 3)
{
}

RealExprRepTan::~RealExprRepTan ()
{
}

void RealExprRepTan::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepTan::copy() const
{
	return new RealExprRepTan(const_child()->copy());
}

//RealExprRepTan&
//RealExprRepTan::operator= (const RealExprRepTan& e) {}


//--------------------------------------------------------------------------
// class RealExprRepTanh
//--------------------------------------------------------------------------
RealExprRepTanh::RealExprRepTanh (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "tanh", 3)
{
}

RealExprRepTanh::RealExprRepTanh (RealExprRep *e) :
	RealExprRepUnaryOp(e, "tanh", 3)
{
}

RealExprRepTanh::RealExprRepTanh (const RealExprRepTanh & e) :
	RealExprRepUnaryOp(e.const_child(), "tanh", 3)
{
}

RealExprRepTanh::~RealExprRepTanh ()
{
}

void RealExprRepTanh::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepTanh::copy() const
{
	return new RealExprRepTanh(const_child()->copy());
}

//RealExprRepTanh&
//RealExprRepTanh::operator= (const RealExprRepTanh& e) {}


//--------------------------------------------------------------------------
// class RealExprRepAtan
//--------------------------------------------------------------------------
RealExprRepATan::RealExprRepATan (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "atan", 3)
{
}

RealExprRepATan::RealExprRepATan (RealExprRep *e) :
	RealExprRepUnaryOp(e, "atan", 3)
{
}

RealExprRepATan::RealExprRepATan (const RealExprRepATan & e) :
	RealExprRepUnaryOp(e.const_child(), "atan", 3)
{
}

RealExprRepATan::~RealExprRepATan ()
{
}

void RealExprRepATan::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepATan::copy() const
{
	return new RealExprRepATan(const_child()->copy());
}

//RealExprRepAtan&
//RealExprRepAtan::operator= (const RealExprRepAtan& e) {}

//--------------------------------------------------------------------------
// class RealExprRepAtanh
//--------------------------------------------------------------------------
RealExprRepATanh::RealExprRepATanh (RealExprRep & e) :
	RealExprRepUnaryOp(&e, "atanh", 3)
{
}

RealExprRepATanh::RealExprRepATanh (RealExprRep *e) :
	RealExprRepUnaryOp(e, "atanh", 3)
{
}

RealExprRepATanh::RealExprRepATanh (const RealExprRepATanh & e) :
	RealExprRepUnaryOp(e.const_child(), "atanh", 3)
{
}

RealExprRepATanh::~RealExprRepATanh ()
{
}

void RealExprRepATanh::visit(RealExprVisitor & v)
{
	v.visited(*this);
}

RealExprRep *RealExprRepATanh::copy() const
{
	return new RealExprRepATanh(const_child()->copy());
}

//RealExprRepAtanh&
//RealExprRepAtanh::operator= (const RealExprRepAtanh& e) {}


//--------------------------------------------------------------------------
// class RealExpr
//--------------------------------------------------------------------------
RealExpr::RealExpr ()
{
	_expr = new RealExprRepConst((double)0);
	_expr->addRef();
}

RealExpr::RealExpr (RealExprRep & expr)
{
	expr.addRef();
	_expr = &expr;
}

RealExpr::RealExpr (RealExprRep *expr)
{
	expr->addRef();
	_expr = expr;
}

/*
RealExpr::RealExpr (const RealConstant & c)
{
	_expr = new RealExprRepConst(c);
	_expr->addRef();
}

RealExpr::RealExpr (const IntervalConstant & c)
{
	_expr = new RealExprRepIntervalConst(c);
	_expr->addRef();
}
*/
RealExpr::RealExpr (const Constant & c)
{
	try {
		_expr = new RealExprRepConst(dynamic_cast<const RealConstant&>(c));
	} catch ( const std::bad_cast& e ) {
		_expr = new RealExprRepIntervalConst(dynamic_cast<const IntervalConstant&>(c));
	}
	_expr->addRef();
}

RealExpr::RealExpr (const int & n)
{
	_expr = new RealExprRepConst(n);
	_expr->addRef();
}
RealExpr::RealExpr (const long long & n)
{
	_expr = new RealExprRepConst(n);
	_expr->addRef();
}

RealExpr::RealExpr (const double & x)
{
	_expr = new RealExprRepConst(x);
	_expr->addRef();
}

RealExpr::RealExpr (Variable & v)
{
	_expr = new RealExprRepVar(v);
	_expr->addRef();
}

RealExpr::RealExpr (const RealExpr & e) : SymbolicExpression(e)
{
	e._expr->addRef();
	_expr = e._expr;
}

RealExpr::~RealExpr ()
{
	_expr->delRef();
}

RealExpr &
RealExpr::operator =(const RealExpr & e)
{
	if (_expr != 0)
		_expr->delRef();
	_expr = e._expr;
	_expr->addRef();
	return *this;
}


RealExpr &
RealExpr::operator+=(const RealExpr & e)
{
	RealExprRepSum *sum_this;
	RealExprRepSum *sum_e;
	RealExprRepAdd *add_this;
	RealExprRepAdd *add_e;
	RealExprRep *temp;
	RealExprRep *expr;
	expr = static_cast<RealExprRep *>(e._expr);

	// Is the new expression just an addition?
	// (if THIS and e are not a sum or an addition)
	if (((sum_this = dynamic_cast<RealExprRepSum *>(_expr)) == 0) &&
	    ((add_this = dynamic_cast<RealExprRepAdd *>(_expr)) == 0) &&
	    ((sum_e = dynamic_cast<RealExprRepSum *>(e._expr)) == 0) &&
	    ((add_e = dynamic_cast<RealExprRepAdd *>(e._expr)) == 0)) {
		temp = static_cast<RealExprRep *>(_expr);
		_expr = new RealExprRepAdd(temp, expr);
		_expr->addRef();
		temp->delRef();
	}
	// otherwise the new expression is a sum
	else {
		// If THIS is not already a sum => transformation
		if ((sum_this = dynamic_cast<RealExprRepSum *>(_expr)) == 0) {
			sum_this = new RealExprRepSum;

			if ((add_this = dynamic_cast<RealExprRepAdd *>(_expr)) != 0) {
				// THIS is an addiion
				sum_this->addChild(add_this->const_left());
				sum_this->addChild(add_this->const_right());
			} else {
				sum_this->addChild(static_cast<RealExprRep *>(_expr));
			}
			_expr->delRef();
			_expr = sum_this;
			_expr->addRef();
		}

		// Is e a sum?
		if ((sum_e = dynamic_cast<RealExprRepSum *>(e._expr)) != 0) {
			for (int i = 1; i <= sum_e->arity(); ++i) {
				sum_this->addChild(sum_e->const_child(i));
			}
		}
		// Is e a an addition?
		else if ((add_e = dynamic_cast<RealExprRepAdd *>(e._expr)) != 0) {
			sum_this->addChild(add_e->const_left());
			sum_this->addChild(add_e->const_right());
		}
		// otherwise
		else {
			sum_this->addChild(expr);
		}
	}

	return *this;
}

RealExpr &
RealExpr::operator-=(const RealExpr & e)
{
	RealExprRep *temp = static_cast<RealExprRep *>(_expr);
	RealExprRep *temp2 = static_cast<RealExprRep *>(e._expr);
	_expr = new RealExprRepSub(temp, temp2);
	_expr->addRef();
	temp->delRef();
	return *this;
}

RealExpr &
RealExpr::operator*=(const RealExpr & e)
{
	RealExprRepProd *prod_this;
	RealExprRepProd *prod_e;
	RealExprRepMul *mul_this;
	RealExprRepMul *mul_e;
	RealExprRep *temp2 = static_cast<RealExprRep *>(e._expr);

	// Is the new expression just a multiplication?
	// (if THIS and e are not a product or a multiplication)
	if (((prod_this = dynamic_cast<RealExprRepProd *>(_expr)) == 0) &&
	    ((mul_this = dynamic_cast<RealExprRepMul *>(_expr)) == 0) &&
	    ((prod_e = dynamic_cast<RealExprRepProd *>(e._expr)) == 0) &&
	    ((mul_e = dynamic_cast<RealExprRepMul *>(e._expr)) == 0)) {
		RealExprRep *temp = static_cast<RealExprRep *>(_expr);
		_expr = new RealExprRepMul(temp, temp2);
		_expr->addRef();
		temp->delRef();
	}
	// otherwise the new expression is a product
	else {
		// If THIS is not already a product => transformation
		if ((prod_this = dynamic_cast<RealExprRepProd *>(_expr)) == 0) {
			prod_this = new RealExprRepProd;
			RealExprRep *temp = static_cast<RealExprRep *>(_expr);

			if ((mul_this = dynamic_cast<RealExprRepMul *>(_expr)) != 0) {
				// THIS is a multiplication
				prod_this->addChild(mul_this->const_left());
				prod_this->addChild(mul_this->const_right());
			} else {
				prod_this->addChild(temp);
			}
			_expr->delRef();
			_expr = prod_this;
			_expr->addRef();
		}

		// Is e a product?
		if ((prod_e = dynamic_cast<RealExprRepProd *>(e._expr)) != 0) {
			for (int i = 1; i <= prod_e->arity(); ++i) {
				prod_this->addChild(prod_e->const_child(i));
			}
		}
		// Is e a an multiplication?
		else if ((mul_e = dynamic_cast<RealExprRepMul *>(e._expr)) != 0) {
			prod_this->addChild(mul_e->const_left());
			prod_this->addChild(mul_e->const_right());
		}
		// otherwise
		else {
			prod_this->addChild(temp2);
		}
	}

	return *this;
}

RealExpr &
RealExpr::operator/=(const RealExpr & e)
{
	RealExprRep *temp = static_cast<RealExprRep *>(_expr);
	RealExprRep *temp2 = static_cast<RealExprRep *>(e._expr);
	_expr = new RealExprRepDiv(temp, temp2);
	_expr->addRef();
	temp->delRef();
	return *this;
}

RealExpr *RealExpr::copy() const
{
	return new RealExpr(static_cast<RealExprRep *>(_expr)->copy());
}

RealExprRep *RealExpr::rep()
{
	return static_cast<RealExprRep *>(_expr);
}

RealExprRep *RealExpr::const_rep() const
{
	return static_cast<RealExprRep *>(_expr);
}



//------------------------------------------------------------------------
// Operators
//------------------------------------------------------------------------

elisa::RealExpr 
elisa::operator+(const elisa::RealExpr & e, const elisa::RealExpr & f)
{
	elisa::RealExprRepSum *sum_e;
	elisa::RealExprRepSum *sum_f;
	elisa::RealExprRepAdd *add_e;
	elisa::RealExprRepAdd *add_f;

	// Is e a sum?
	if ((sum_e = dynamic_cast<elisa::RealExprRepSum *>(e.const_rep())) != 0) {
		if ((sum_f = dynamic_cast<elisa::RealExprRepSum *>(f.const_rep())) != 0) {
			// sum_e + sum_f => the children of sum_f are just added in sum_e
			for (int i = 1; i <= sum_f->arity(); ++i) {
				sum_e->addChild(sum_f->const_child(i));
			}
		} else if ((add_f = dynamic_cast<elisa::RealExprRepAdd *>(f.const_rep())) != 0) {
			// sum_e + add_f => the children of add_f are just added in sum_e
			sum_e->addChild(add_f->const_left());
			sum_e->addChild(add_f->const_right());
		} else {
			// sum_e + f => f is just added in sum_e
			sum_e->addChild(f.const_rep());
		}
		return RealExpr(e.const_rep());
	}
	// Is f a sum?
	else  if ((sum_f = dynamic_cast<elisa::RealExprRepSum *>(f.const_rep())) != 0) {
		if ((add_e = dynamic_cast<elisa::RealExprRepAdd *>(e.const_rep())) != 0) {
			// add_e + sum_f => the children of add_e are just added in sum_f
			sum_f->addChild(add_e->const_left());
			sum_f->addChild(add_e->const_right());
		} else {
			// e + sum_f => e is just added in sum_f
			sum_f->addChild(e.const_rep());
		}
		return RealExpr(f.const_rep());
	}
	// Is e an addition?
	else if ((add_e = dynamic_cast<elisa::RealExprRepAdd *>(e.const_rep())) != 0) {
		sum_e = new elisa::RealExprRepSum;
		sum_e->addChild(add_e->const_left());
		sum_e->addChild(add_e->const_right());

		// Is f an addition?
		if ((add_f = dynamic_cast<elisa::RealExprRepAdd *>(f.const_rep())) != 0) {
			// add_e + add_f
			sum_e->addChild(add_f->const_left());
			sum_e->addChild(add_f->const_right());
		} else {
			// add_e + f
			sum_e->addChild(f.const_rep());
		}
		return RealExpr(sum_e);
	}
	// Is f an addition?
	else if ((add_f = dynamic_cast<elisa::RealExprRepAdd *>(f.const_rep())) != 0) {
		// e + add_f
		sum_f = new elisa::RealExprRepSum;
		sum_f->addChild(e.const_rep());
		sum_f->addChild(add_f->const_left());
		sum_f->addChild(add_f->const_right());
		return RealExpr(sum_f);
	} else {
		return RealExpr(new elisa::RealExprRepAdd(e.const_rep(),
		                                          f.const_rep()));
	}
}

elisa::RealExpr
elisa::operator-(const elisa::RealExpr& e, const elisa::RealExpr& f)
{
	return RealExpr(new elisa::RealExprRepSub(e.const_rep(),
	                                          f.const_rep()));
}

elisa::RealExpr
elisa::operator-(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepOpp(e.const_rep()));
}

elisa::RealExpr
elisa::operator*(const elisa::RealExpr & e,
          const elisa::RealExpr & f)
{
	elisa::RealExprRepProd *prod_e;
	elisa::RealExprRepProd *prod_f;
	elisa::RealExprRepMul *mul_e;
	elisa::RealExprRepMul *mul_f;
	elisa::RealExprRepProd *prod_res;

	// Is e a product?
	if ((prod_e = dynamic_cast<elisa::RealExprRepProd *>(e.const_rep())) != 0) {
		if ((prod_f = dynamic_cast<elisa::RealExprRepProd *>(f.const_rep())) != 0) {
			prod_res = new elisa::RealExprRepProd;

			for (int i = 1; i <= prod_e->arity(); ++i) {
				prod_res->addChild(prod_e->const_child(i));
			}

			for (int i = 1; i <= prod_f->arity(); ++i) {
				prod_res->addChild(prod_f->const_child(i));
			}
		} else if ((mul_f = dynamic_cast<elisa::RealExprRepMul *>(f.const_rep())) != 0) {
			prod_res = new elisa::RealExprRepProd;

			// prod_e * mul_f => the children of mul_f are just added in prod_e
			for (int i = 1; i <= prod_e->arity(); ++i) {
				prod_res->addChild(prod_e->const_child(i));
			}
			prod_res->addChild(mul_f->const_left());
			prod_res->addChild(mul_f->const_right());
		} else {
			prod_res = new elisa::RealExprRepProd;

			// prod_e * mul_f => the children of mul_f are just added in prod_e
			for (int i = 1; i <= prod_e->arity(); ++i) {
				prod_res->addChild(prod_e->const_child(i));
			}
			// prod_e * f => f is just added in prod_e
			prod_res->addChild(f.const_rep());
		}
		return RealExpr(prod_res);
	}
	// Is f a product?
	else  if ((prod_f = dynamic_cast<elisa::RealExprRepProd *>(f.const_rep())) != 0) {
		if ((mul_e = dynamic_cast<elisa::RealExprRepMul *>(e.const_rep())) != 0) {
			prod_res = new elisa::RealExprRepProd;
			for (int i = 1; i <= prod_f->arity(); ++i) {
				prod_res->addChild(prod_f->const_child(i));
			}

			// mul_e * prod_f => the children of mul_e are just added in prod_f
			prod_res->addChild(mul_e->const_left());
			prod_res->addChild(mul_e->const_right());
		} else {
			prod_res = new elisa::RealExprRepProd;
			for (int i = 1; i <= prod_f->arity(); ++i) {
				prod_res->addChild(prod_f->const_child(i));
			}

			// e * prod_f => e is just added in prod_f
			prod_res->addChild(e.const_rep());
		}
		return RealExpr(prod_res);
	}
	// Is e a multiplication?
	else if ((mul_e = dynamic_cast<elisa::RealExprRepMul *>(e.const_rep())) != 0) {
		prod_e = new elisa::RealExprRepProd;
		prod_e->addChild(mul_e->const_left());
		prod_e->addChild(mul_e->const_right());

		// Is f a multiplication?
		if ((mul_f = dynamic_cast<elisa::RealExprRepMul *>(f.const_rep())) != 0) {
			// mul_e * mul_f
			prod_e->addChild(mul_f->const_left());
			prod_e->addChild(mul_f->const_right());
		} else {
			// mul_e * f
			prod_e->addChild(f.const_rep());
		}
		return RealExpr(prod_e);
	}
	// Is f a multiplication?
	else if ((mul_f = dynamic_cast<elisa::RealExprRepMul *>(f.const_rep())) != 0) {
		// e * mul_f
		prod_f = new elisa::RealExprRepProd;
		prod_f->addChild(e.const_rep());
		prod_f->addChild(mul_f->const_left());
		prod_f->addChild(mul_f->const_right());
		return RealExpr(prod_f);
	} else {
		return RealExpr(new elisa::RealExprRepMul(e.const_rep(),
		                                          f.const_rep()));
	}
}

elisa::RealExpr
elisa::operator/(const elisa::RealExpr & e,
          const elisa::RealExpr & f)
{
	return RealExpr(new elisa::RealExprRepDiv(e.const_rep(),
	                                          f.const_rep()));
}

elisa::RealExpr elisa::sqr(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepSqr(e.const_rep()));
}

elisa::RealExpr elisa::sqrt(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepSqrt(e.const_rep()));
}

elisa::RealExpr elisa::pow(const elisa::RealExpr & e, const int & n)
{
	return RealExpr(new elisa::RealExprRepPow(e.const_rep(), n));
}

elisa::RealExpr elisa::log(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepLog(e.const_rep()));
}

elisa::RealExpr elisa::exp(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepExp(e.const_rep()));
}

elisa::RealExpr elisa::cos(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepCos(e.const_rep()));
}

elisa::RealExpr elisa::acos(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepACos(e.const_rep()));
}

elisa::RealExpr elisa::sin(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepSin(e.const_rep()));
}

elisa::RealExpr elisa::asin(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepASin(e.const_rep()));
}

elisa::RealExpr elisa::tan(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepTan(e.const_rep()));
}

elisa::RealExpr elisa::atan(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepATan(e.const_rep()));
}

elisa::RealExpr elisa::cosh(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepCosh(e.const_rep()));
}

elisa::RealExpr elisa::acosh(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepACosh(e.const_rep()));
}

elisa::RealExpr elisa::sinh(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepSinh(e.const_rep()));
}

elisa::RealExpr elisa::asinh(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepASinh(e.const_rep()));
}

elisa::RealExpr elisa::tanh(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepTanh(e.const_rep()));
}

elisa::RealExpr elisa::atanh(const elisa::RealExpr & e)
{
	return RealExpr(new elisa::RealExprRepATanh(e.const_rep()));
}
